import json
import pprint
from typing import Union, Optional, List

from fastapi import APIRouter, Query, Depends
from pydantic import BaseModel
from elasticsearch.helpers import async_scan
from api.dataapi.mapping import INST_LABEL
from core.es import Index_KJJ_ACTIVITY_REGISTER, Index_KJJ_DEPARTMENT, Index_KJJ_ALUMNI
from utils.deps import jwt_auth
from utils.es import es_client

simple_router = APIRouter(tags=["搜索"], dependencies=[Depends(jwt_auth)])


async def get_person_ids(name):
    query = {
        "size": 10000,
        "_source": "id",
        "query": {"match_phrase": {"name": name}}}

    resp = async_scan(es_client, index=Index_KJJ_ALUMNI, body=query, scroll="6s",
                      size=3000)

    person_ids = []
    async for doc in resp:
        person_ids.append(doc["_source"].get("id"))

    return person_ids


@simple_router.get("/search/activity", summary="常规搜索")
async def search(
        # 姓名or活动名
        q: Union[str, None] = Query(default=None, max_length=128),
        size: int = Query(default=10, alias="pageSize"),
        page: int = Query(default=1, alias="pageNum"),
        activity_name=Query(default=None, description="机构", alias="act_name"),
        institution_name: Optional[str] = Query(default=None, description="机构", alias="rel_org"),
        institution_op: Optional[str] = Query(default="o", description="机构", alias="rel_org_logic"),
        alumni_name: Optional[str] = Query(default=None, description="人姓名", alias="part_talent"),
        alumni_name_op: Optional[str] = Query(default="o", description="人姓名", alias="part_talent_logic"),
        # facet
        by_year: Optional[str] = Query(default=None, alias="activityYearCount", description="内部机构"),
        by_region: Optional[str] = Query(default=None, alias="areaCount", description="内部机构"),

):
    if activity_name:
        activity_name = activity_name.strip()

    if alumni_name:
        alumni_name = alumni_name.strip()

    must_query = []
    should_query = []
    filter_query = []
    query = {"from": (page - 1) * size, "size": size, "query": {"bool": {"must": must_query,
                                                                         "filter": filter_query,
                                                                         "should": should_query}}}
    # relation activity parent
    # must_query.append(
    #     {"term": {"activity_member": {"value": "activity"}}},
    # )

    if q:
        keyword_stage = {"match_phrase": {"name": q}}
        must_query.append(keyword_stage)

    else:
        must_query.append(
            {"term": {"activity_member": {"value": "activity"}}},
        )

    if by_year:
        filter_query.append({"term": {"range_year": by_year}})
    if by_region:
        filter_query.append({"term": {"region.keyword": by_region}})
    # 处理 a,b, c condition
    # 机构的query expr

    institution_query = {"bool": {"should": [
        {"match": {"host_label.institution_name.keyword": institution_name}},
        {
            "nested": {
                "path": "coop_labels",
                "query": {
                    "term": {
                        "coop_labels.institution_name.keyword": institution_name
                    }
                }
            }
        }
    ]}}

    lambda_member_query = lambda name: {"has_child": {"type": "member", "query": {"terms": {"person_id": name}}}}

    if alumni_name:
        person_ids = await get_person_ids(alumni_name)

    # 处理且或或的关系

    if not activity_name:
        # b&c
        if institution_name and "and" in alumni_name_op:
            person_ids = await get_person_ids(alumni_name)
            must_query.append(institution_query)
            must_query.append(lambda_member_query(person_ids))

        # b or c
        if institution_name and "or" in alumni_name_op:
            should_query.append(institution_query)
            if person_ids:
                should_query.append(lambda_member_query(person_ids))
            query['query']['bool']['must'] = []
        # b存在 a=c=one
        if institution_name and not alumni_name:
            if "and" in institution_op:
                must_query.append(institution_query)

            if "or" in institution_op:
                must_query.append(institution_query)
        # c 存在 a=b=None
        if alumni_name and not institution_name:
            # 忽略c op
            must_query.append(lambda_member_query(person_ids))

    if activity_name:
        if not q:
            must_query.append({"match_phrase": {"name": activity_name}})

        if not institution_name and not alumni_name:
            must_query.append({"match_phrase": {"name": activity_name}})
        # a, b, c
        if "and" in institution_op and institution_name and "and" in alumni_name_op:
            if institution_query:
                must_query.append(institution_query)
            if person_ids:
                must_query.append(lambda_member_query(person_ids))

        # a or b or c
        if "or" in institution_op and institution_name and "or" in alumni_name_op:
            query['query']['bool']['must'] = []
            should_query.append({"match_phrase": {"name": activity_name}})
            if institution_query:
                should_query.append(institution_query)
            if person_ids:
                should_query.append(lambda_member_query(person_ids))
        # (a and b) or c
        if "and" in institution_op and "or" in alumni_name_op and alumni_name:
            must_query.append(institution_query)
            should_query.append(lambda_member_query(person_ids))
            should_query.append({"bool": {"must": must_query}})
            query['query']['bool']['must'] = []

        # a or (b & c)
        if "or" in institution_op and "and" in alumni_name_op and alumni_name:
            # fixme 特色的情况
            _must = []
            _must_query = {"bool": {"must": _must}}
            _must.append(institution_query)
            _must.append(lambda_member_query(person_ids))
            should_query.append({"match_phrase": {"name": activity_name}})
            should_query.append(_must_query)
            query['query']['bool']['must'] = []

        # a, b, c=none
        if institution_name and not alumni_name:
            if "and" in institution_op:
                must_query.append({"bool": {"should": institution_query}})

            if "or" in institution_op:
                # a or b
                should_query.append({"match_phrase": {"name": activity_name}})
                should_query.append(institution_query)
                query['query']['bool']['must'] = []

        # a,c ,b=none
        if alumni_name and not institution_name:
            person_ids = await get_person_ids(alumni_name)

            if "and" in alumni_name_op:
                # don't care none
                must_query.append(lambda_member_query(person_ids))
            # a or c
            if "or" in alumni_name_op:
                query['query']['bool']['must'] = []
                person_ids = await get_person_ids(alumni_name)
                if person_ids:
                    should_query.append(lambda_member_query(person_ids))
                    should_query.append({"match_phrase_prefix": {"name": activity_name}})
                else:
                    # a c(nv)
                    query['query']['bool']['must'].append({"match_phrase_prefix": {"name": activity_name}})

    agg_stage = {
        "by_years": {"terms": {"field": "range_year"}},
        "by_regions": {"terms": {"field": "region.keyword"}},
    }
    if agg_stage:
        query["aggs"] = agg_stage
    print(query)
    res = await es_client.search(index=Index_KJJ_ACTIVITY_REGISTER, body=query)

    items = res["hits"]["hits"]
    total = res["hits"]["total"]["value"]
    aggs = res["aggregations"]
    for item in items:
        number_of_applicants = item["_source"].get("number_of_applicants")

        item["_source"]["numOfParticipants"] = number_of_applicants

    return {"total": total, "items": items, "aggs": aggs}


async def get_activity_and_institution(
        activity_name: Optional[str] = None, inst: Optional[str] = None, activity_id: str = None
) -> Optional[List[str]]:
    """从活动或机构找人 分别查询"""
    if activity_name:
        must_query = {"match_phrase": {"name": activity_name}}

    if inst:
        # fixme add.keyword modifier
        must_query = {"bool": {"should": [
            {"match": {"host_label.institution_name.keyword": inst}},
            {"match": {"coop_labels.institution_name.keyword": inst}},
        ]}}
    # todo 有待优化profile
    query = {"_source": "person_id",
             "size": 9700,
             "query": {"has_parent": {"parent_type": "activity", "query": must_query}}}
    res = await es_client.search(index=Index_KJJ_ACTIVITY_REGISTER, body=query)
    items = res["hits"]["hits"]
    person_ids = []
    for item in items:
        person_ids.append(item.get("_source").get("person_id"))
    return person_ids


@simple_router.get("/search/alumni", summary="搜索激活人才")
async def search(
        # 姓名or活动名
        q: Union[str, None] = Query(default=None, max_length=128),
        size: int = Query(default=10, alias="pageSize"),
        page: int = Query(default=1, alias="pageNum"),
        alumni_name: Optional[str] = Query(default=None, description="活动", alias="talent_name"),
        activity_name: Optional[str] = Query(default=None, description="活动", alias="activity"),
        activity_name_op: Optional[str] = Query(default="op", description="活动", alias="activity_logic"),
        institution_name: Optional[str] = Query(default=None, description="机构", alias="relation_org"),
        institution_op: Optional[str] = Query(default="op", description="机构", alias="relation_org_logic"),
        gender: Optional[str] = Query(default=None, alias="sexCount", description="性别"),
        region: Optional[str] = Query(default=None, alias="areaCount", description="地区"),
):
    if alumni_name:
        alumni_name = alumni_name.strip()
    if activity_name:
        activity_name = activity_name.strip()

    must_query = []
    should_query = []
    filter_query = [
        {
            "range": {
                "number_of_applicants": {
                    "gt": 0
                }
            }
        }
    ]
    query = {"from": (page - 1) * size, "size": size, "query": {"bool": {"must": must_query,
                                                                         "filter": filter_query,
                                                                         "should": should_query}}}
    if q:
        keyword_stage = {
            "multi_match": {
                "query": q,
                "type": "phrase",
                "fields": ["name", "name_pinyin"]
            }
        }

        must_query.append(keyword_stage)

    else:
        keyword_stage = {"match_all": {}}
        must_query.append(keyword_stage)

    # facet filed
    if gender:
        gender_stage = {"term": {"gender": gender}}
        filter_query.append(gender_stage)
    if region:
        region_stage = {"term": {"region.keyword": region}}
        filter_query.append(region_stage)

    if alumni_name:
        alumni_stage = {"match_phrase_prefix": {"name": alumni_name}}

    # 12情况
    # 处理且或或的关系
    # 活动
    activity_belong_persons = []
    if activity_name:
        activity_belong_persons = await get_activity_and_institution(activity_name=activity_name)
    # 机构
    institution_c = []
    belong_inst_query = {"term": {"belongs_inst": INST_LABEL.get(institution_name)}}

    # case 1 A 不存在情况，考虑b,c
    if not alumni_name:
        # b&c (忽略b op)
        if activity_name and "and" in institution_op:
            if activity_belong_persons:
                must_query.append({"ids": {"values": activity_belong_persons}})
            must_query.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})

        # b or c (忽略b op)
        if activity_name and "or" in institution_op:
            if activity_belong_persons:
                should_query.append({"ids": {"values": activity_belong_persons}})

                should_query.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})
                query['query']['bool']['must'] = []
            else:
                must_query.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})

        # b存在 c=none
        if activity_name and not institution_name:
            # if "and" in activity_name_op:
            #     must_query.append({"ids": {"values": activity_belong_persons}})
            # # an or b
            # if "or" in activity_name_op:
            #     if activity_belong_persons:
            #         should_query.append({"ids": {"values": activity_belong_persons}})
            must_query.append({"ids": {"values": activity_belong_persons}})

        # c 存在 b = None
        if institution_name and not activity_name:
            if "and" in institution_op:
                must_query.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})

            if "or" in institution_op:
                # 就有一个值的情况, 操作法没有实在的意义了
                # 保留，readable
                must_query.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})

    # a必填 (b&c) 必须优质
    if alumni_name:
        # 必须存在 q
        if not q:
            must_query.append({"multi_match": {
                "query": alumni_name,
                "type": "phrase",
                "fields": ["name", "name_pinyin"]
            }})

        # a, b, c
        if "and" in activity_name_op and "and" in institution_op:
            person_ids = set(activity_belong_persons) & set(institution_c)
            must_query.append({"ids": {"values": activity_belong_persons}})
            must_query.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})

        # a or b or c
        if "or" in activity_name_op and "or" in institution_op:
            query['query']['bool']['must'] = []
            should_query.append({"multi_match": {
                "query": alumni_name,
                "type": "phrase",
                "fields": ["name", "name_pinyin"]
            }})
            if activity_belong_persons:
                should_query.append({"ids": {"values": list(activity_belong_persons)}})
            should_query.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})
        # a or (b & c)
        if "or" in activity_name_op and "and" in institution_op:
            _must = []
            _must_query = {"bool": {"must": _must}}
            _must.append({"ids": {"values": activity_belong_persons}})
            _must.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})
            should_query.append({"multi_match": {
                "query": alumni_name,
                "type": "phrase",
                "fields": ["name", "name_pinyin"]
            }})
            should_query.append(_must_query)
            query['query']['bool']['must'] = []

        # (a and b) or c
        if "and" in activity_name_op and "or" in institution_op and institution_name:
            must_query.append({"ids": {"values": activity_belong_persons}})
            should_query.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})
            should_query.append({"bool": {"must": must_query}})
            query['query']['bool']['must'] = []
        # a, b, c=none
        if activity_name and not institution_name:
            # a and b
            if "and" in activity_name_op:
                must_query.append({"ids": {"values": activity_belong_persons}})

            if "or" in activity_name_op:
                if activity_belong_persons:
                    query['query']['bool']['must'] = []
                    should_query.append({"multi_match": {
                        "query": alumni_name,
                        "type": "phrase",
                        "fields": ["name", "name_pinyin"]
                    }})
                    should_query.append({"ids": {"values": activity_belong_persons}})

        # a,c ,b=none
        if institution_name and not activity_name:
            if "and" in institution_op:
                must_query.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})

            if "or" in institution_op:
                # a or c
                query['query']['bool']['must'] = []
                should_query.append({"multi_match": {
                    "query": alumni_name,
                    "type": "phrase",
                    "fields": ["name", "name_pinyin"]
                }})
                should_query.append({"term": {"belongs_inst": INST_LABEL.get(institution_name)}})

    agg = {"by_gender": {"terms": {"field": "gender.keyword"}}, "by_regions": {"terms": {"field": "region.keyword"}}}
    query["aggs"] = agg
    res = await es_client.search(index=Index_KJJ_ALUMNI, body=query)

    items = res["hits"]["hits"]
    total = res["hits"]["total"]["value"]
    aggs = res["aggregations"]

    return {"total": total, "items": items, "aggs": aggs}


@simple_router.get("/search/institution", summary="机构部门")
async def search(
        # 姓名or活动名
        q: Union[str, None] = Query(default=None, max_length=128),
        size: int = Query(default=10, alias="pageSize"),
        page: int = Query(default=1, alias="pageNum"),
        activity_label: Optional[int] = Query(default=None, description="活动"),
        in_kexie: Optional[str] = Query(default=None, description="内部机构"),
):
    must_query = []
    filter_query = []

    query = {"from": (page - 1) * size, "size": size,
             "_source": {"excludes": ["m_*"]},  # exclude metric field
             "query": {"bool": {"must": must_query, "filter": filter_query}}}
    if q:
        keyword_stage = {"match_phrase": {"institution_name": q}}
        must_query.append(keyword_stage)
    else:
        keyword_stage = {"match_all": {}}
        must_query.append(keyword_stage)

    if in_kexie:
        filter_query.extend([{"term": {"in_kexie": True}},
                             {"terms": {"level": [1, 2]}}])

    query["aggs"] = {
        "kexie_self": {
            "filter": {"term": {
                "level": 1
            }},
            "aggs": {
                "by_kexie": {
                    "terms": {
                        "field": "level"
                    }
                }
            }

        }
    }

    res = await es_client.search(index=Index_KJJ_DEPARTMENT, body=query)

    items = res["hits"]["hits"]
    total = res["hits"]["total"]["value"]
    aggs = res["aggregations"]['kexie_self']
    print(aggs)
    return {"items": items, "total": total, "aggs": aggs}


class FilterModel(BaseModel):
    activity_id: Optional[str]
    is_attended: bool = True


class RegModel(BaseModel):
    offset: int = 0
    size: int = 20
    filter: FilterModel


@simple_router.post("/activity/register", description="机构下办的活动")
async def search_activity(item: RegModel, id: str = "62b945cd9aebef53937fecca"):
    """id 机构id"""
    institution_id = id
    print(item)
    query = {
        "from": item.size * item.offset,
        "size": item.size,
        "query": {
            "bool": {
                "must": [
                    {"has_child": {"type": "member", "query": {"term": {"is_attended": {"value": True}}}}},
                    {"match": {"host_label.id": institution_id}},
                ]
            }
        },
    }

    res = await es_client.search(index=Index_KJJ_ACTIVITY_REGISTER, body=query)

    items = res["hits"]["hits"]
    total = res["hits"]["total"]["value"]
    sources = []
    for item in items:
        sources.append(item["_source"])
    return {"total": total, "items": sources, "aggs": []}
